﻿using ChongJu.CloudService;
using ChongJu.EventManager;
using ChongJu.GlobalInfo;
using ChongJu.ModelManager.Camera;
using MvCamCtrl.NET;
using Public.Logger;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using static MvCamCtrl.NET.MyCamera;

namespace ChongJu.Camera.MvCameraControl
{
    public class MvCamera : ICameraBase
    {
        private MyCamera m_pMyCamera;
        private MyCamera.MV_CC_DEVICE_INFO_LIST m_pDeviceList;
        private bool cameraOpened = false; //相机是否打开
        private bool grabbing = false; //是否在采集照片中
        private CameraModel _cfg;
        private CloudService.CloudService _cloud;

        public MyCamera.cbOutputExdelegate ImageCallback;
        public event Action<Bitmap, DateTime> ImageCallBackEvent;

        public bool Init(CameraModel cfg, CloudService.CloudService cloud)  //只获取第一台USB相机
        {
            try
            {
                _cfg = cfg;
                _cloud = cloud;

                m_pDeviceList = new MyCamera.MV_CC_DEVICE_INFO_LIST();
                int nRet;
                nRet = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref m_pDeviceList);
                if (MyCamera.MV_OK != nRet)
                {
                    Logger.Error("枚举相机失败！");
                }
                Logger.Debug("相机数量：" + m_pDeviceList.nDeviceNum);
                if (m_pDeviceList.nDeviceNum < 1)
                {
                    Logger.Error("未能检测到相机！");
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                Logger.Error("初始化相机异常", ex);
                _cloud.UploadLog("初始化相机异常：" + ex.Message, GlobalInfo.GlobalVar.DeviceID);
                return false;
            }
        }

        public bool Open()
        {
            try
            {
                if (cameraOpened)
                    return true;

                if (m_pMyCamera == null)
                    m_pMyCamera = new MyCamera();

                MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(
                    m_pDeviceList.pDeviceInfo[0],
                    typeof(MyCamera.MV_CC_DEVICE_INFO));

                int nRet = m_pMyCamera.MV_CC_CreateDevice_NET(ref device);
                if (MyCamera.MV_OK != nRet)
                {
                    Logger.Error("相机创建失败！");
                    return false;
                }

                nRet = m_pMyCamera.MV_CC_OpenDevice_NET();
                if (MyCamera.MV_OK != nRet)
                {
                    m_pMyCamera.MV_CC_DestroyDevice_NET();
                    Logger.Error("相机打开失败！");
                    return false;
                }

                cameraOpened = true;

                StopCrab();
                //软件触发模式
                RegistImageCallback();
                SetParams(_cfg, true);
                SetDefautParam();
                StartCrab();

                return true;
            }
            catch (Exception ex)
            {
                cameraOpened = false;
                Logger.Error("打开相机异常", ex);
                _cloud.UploadLog("打开相机异常：" + ex.Message, GlobalInfo.GlobalVar.DeviceID);
                return false;
            }
        }

        public bool Close()
        {
            try
            {
                int nRet = m_pMyCamera.MV_CC_CloseDevice_NET();
                if (MyCamera.MV_OK != nRet)
                {
                    m_pMyCamera.MV_CC_DestroyDevice_NET();
                    Logger.Error("相机关闭失败！");
                    return false;
                }

                return true;
            }
            catch (Exception ex)
            {
                Logger.Error("关闭相机异常", ex);
                return false;
            }
        }

        public void OpenTriggerMode()
        {
            int nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("TriggerMode", 1);
            if (MyCamera.MV_OK != nRet)
            {
                Logger.Error("触发模式打开失败！");
            }
        }

        public void CloseTriggerMode()
        {
            int nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("TriggerMode", 0);
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("触发模式关闭失败！");
            }
        }

        public void StartCrab()
        {
            if (grabbing)
                return;
            int nRet = m_pMyCamera.MV_CC_StartGrabbing_NET();
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("采集开始失败！");
            }
            grabbing = true;
        }

        public void StopCrab()
        {
            if (!grabbing)
                return;
            int nRet = m_pMyCamera.MV_CC_StopGrabbing_NET();
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("采集停止失败！");
            }
            grabbing = false;
        }

        public void SetTriggerByLine(int idx = 0)
        {
            SetTriggerSource(idx);
        }

        public void SetTriggerBySoftware()
        {
            SetTriggerSource(7);
        }

        //执行一次软件触发
        public void ExecTriggerBySoftware()
        {
            int nRet;

            // ch:触发命令 | en:Trigger command
            nRet = m_pMyCamera.MV_CC_SetCommandValue_NET("TriggerSoftware");
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("触发拍照失败");
            }
        }

        //           0 - Line0;
        //           1 - Line1;
        //           2 - Line2;
        //           3 - Line3;
        //           4 - Counter;
        //           7 - Software;
        private void SetTriggerSource(int source)
        {
            int nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("TriggerSource", (uint)source);
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("设置触发源" + source + "失败！");
            }
        }

        public void SetDefautParam()
        {
            //if (grabbing)
            //    StopCrab();

            int nRet;
            nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("AcquisitionMode", 2);  //连续模式
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("连续模式设置失败！");
            }
            nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("GainAuto", 0);  //关闭自动增益
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("关闭自动增益失败！");
            }
            nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("TriggerMode", 1);
            if (MyCamera.MV_OK != nRet)
            {
                Logger.Error("触发模式打开失败！");
            }
            nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("TriggerSource", (uint)0);
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("设置触发源" + 0 + "失败！");
            }
            nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("BalanceWhiteAuto", 0);  //关闭白平衡
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("关闭白平衡写入失败！");
            }
            nRet = m_pMyCamera.MV_CC_SetBoolValue_NET("AcquisitionFrameRateEnable", true);      //帧速率设置使能
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("帧速率设置使能失败！");
            }

            nRet = m_pMyCamera.MV_CC_SetFloatValue_NET("AcquisitionFrameRate", 30f);      //帧速率设置
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("帧速率设置设置失败！");
            }
            //nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("LineMode", 8);      //IO Line1模式
            //if (MyCamera.MV_OK != nRet)
            //{
            //    throw new Exception("IO Line1模式设置失败！");
            //}

            if (GlobalVar.GlobalParamer.IsBottom == false)
                OpenLine2CloseLine1();
            else
                OpenLine1CloseLine2();

            //nRet = m_pMyCamera.MV_CC_SetGamma_NET(1); //Gamma使能打开
            //if (nRet != MyCamera.MV_OK)
            //    throw new Exception("gamma 使能打开失败");

            //nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("PixelFormat", 0x02180014);      //像素格式
            //if (MyCamera.MV_OK != nRet)
            //{
            //    throw new Exception("像素格式设置失败！");
            //}
        }

        public void SetParams(CameraModel cfg, bool setWidthAndHeight)
        {
            //if (grabbing)
            //    StopCrab();

            float ExposureTime = (float)cfg.Exposure; //ms
            float Gain = cfg.Gain;
            float TriggerDelay = cfg.TriggerDelay * 1000; //ms
            int RedGain = cfg.RedGain;
            int GreenGain = cfg.GreenGain;
            int BlueGain = cfg.BlueGain;

            int nRet;
            nRet = m_pMyCamera.MV_CC_SetFloatValue_NET("ExposureTime", ExposureTime);         //设置曝光时长
            if (MyCamera.MV_OK != nRet)
                throw new Exception("设置曝光时长失败！");

            nRet = m_pMyCamera.MV_CC_SetFloatValue_NET("Gain", Gain);                     //设置增益
            if (MyCamera.MV_OK != nRet)
                throw new Exception("设置增益失败！");
            nRet = m_pMyCamera.MV_CC_SetFloatValue_NET("TriggerDelay", TriggerDelay);      //设置触发延迟
            if (MyCamera.MV_OK != nRet)
                throw new Exception("设置触发延迟失败！");

            //关闭自动调节白平衡，不然后续不能设置RGB
            nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("BalanceWhiteAuto", 0);
            if (MyCamera.MV_OK != nRet)
                throw new Exception("关闭自动调节白平衡失败！");

            nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("BalanceRatioSelector", 0);                            //RedGain
            nRet += m_pMyCamera.MV_CC_SetIntValueEx_NET("BalanceRatio", RedGain);
            if (MyCamera.MV_OK != nRet)
                throw new Exception("设置RedGain失败！");

            nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("BalanceRatioSelector", 1);
            nRet += m_pMyCamera.MV_CC_SetIntValueEx_NET("BalanceRatio", GreenGain);                     //GreenGain
            if (MyCamera.MV_OK != nRet)
                throw new Exception("GreenGain失败！");

            nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("BalanceRatioSelector", 2);
            nRet += m_pMyCamera.MV_CC_SetIntValueEx_NET("BalanceRatio", BlueGain);      //BlueGain
            if (MyCamera.MV_OK != nRet)
                throw new Exception("BlueGain失败！");

            //由于相机的宽和高只能StopGrabbing的时候才能使用，但是并不是所有地方都要改宽和高，所以抽出一个控制操作的功能
            //当需要设置宽和高的时候先关掉（调用 StopCrab()方法），设置完之后再开启（调用StartCrab()方法）即可
            if (setWidthAndHeight)
            {
                //if (grabbing)
                    //StopCrab();

                nRet = m_pMyCamera.MV_CC_SetIntValueEx_NET("OffsetX", 0);      //ROI-X
                if (MyCamera.MV_OK != nRet)
                    throw new Exception("设置ROI-X失败！");

                nRet = m_pMyCamera.MV_CC_SetIntValueEx_NET("OffsetY", 0);      //ROI-Y
                if (MyCamera.MV_OK != nRet)
                    throw new Exception("设置ROI-Y失败！");

                nRet = m_pMyCamera.MV_CC_SetIntValueEx_NET("Width", cfg.Width);      //ROI宽度
                if (MyCamera.MV_OK != nRet)
                    throw new Exception("ROI宽度设置失败！");

                nRet = m_pMyCamera.MV_CC_SetIntValueEx_NET("Height", cfg.Height);      //ROI高度
                if (MyCamera.MV_OK != nRet)
                    throw new Exception("ROI高度设置失败！");
            }

            nRet = m_pMyCamera.MV_CC_SetIntValueEx_NET("OffsetX", cfg.ROI_X);      //ROI-X
            if (MyCamera.MV_OK != nRet)
                throw new Exception("设置ROI-X失败！");

            nRet = m_pMyCamera.MV_CC_SetIntValueEx_NET("OffsetY", cfg.ROI_Y);      //ROI-Y
            if (MyCamera.MV_OK != nRet)
                throw new Exception("设置ROI-Y失败！");

            //StartCrab();
        }

        public string SetCalibrationParms(CameraModel cfg)
        {
            StopCrab();
            //var nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("LineSelector", 1);      //IO Line1
            //nRet += m_pMyCamera.MV_CC_SetIntValueEx_NET("StrobeLineDuration", cfg.OutDelay); //输出延时时间 
            //if (nRet != 0)
            //    return "设置输出延时失败";

            var nRet = m_pMyCamera.MV_CC_SetFloatValue_NET("ExposureTime", cfg.Exposure);   //设置曝光时长 ms -> us
            if (nRet != 0)
                return "设置曝光时间失败";

            nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("BalanceRatioSelector", 0);                            //RedGain
            nRet += m_pMyCamera.MV_CC_SetIntValueEx_NET("BalanceRatio", cfg.RedGain);
            if (MyCamera.MV_OK != nRet)
                return "设置RedGain失败！";

            nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("BalanceRatioSelector", 1);
            nRet += m_pMyCamera.MV_CC_SetIntValueEx_NET("BalanceRatio", cfg.GreenGain);                     //GreenGain
            if (MyCamera.MV_OK != nRet)
                return "设置GreenGain失败";

            nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("BalanceRatioSelector", 2);
            nRet += m_pMyCamera.MV_CC_SetIntValueEx_NET("BalanceRatio", cfg.BlueGain);      //BlueGain
            if (MyCamera.MV_OK != nRet)
                return "设置BlueGain失败";

            StartCrab();
            return null;
        }

        public void RegistImageCallback()
        {
            ImageCallback = new MyCamera.cbOutputExdelegate(OnImageGrabbed);
            int nRet = m_pMyCamera.MV_CC_RegisterImageCallBackForBGR_NET(ImageCallback, IntPtr.Zero);
            if (MyCamera.MV_OK != nRet)
            {
                Console.WriteLine("Register image callback failed!");
            }
        }

        public void OnImageGrabbed(IntPtr pData, ref MyCamera.MV_FRAME_OUT_INFO_EX pFrameInfo, IntPtr pUser)
        {
            //if (ImageCallback == null)
            //    return;
            //CommonEvent.OnRunLogger(pFrameInfo.nInput + "-" + pFrameInfo.nInput);
            DateTime time = DateTime.Now;
            Bitmap bmp = new Bitmap(pFrameInfo.nWidth, pFrameInfo.nHeight, 
                pFrameInfo.nWidth * 3, PixelFormat.Format24bppRgb, pData);
            ImageCallBackEvent?.Invoke(bmp, time);
        }

        /****************************************************************************
         * @fn           GetEnumValue
         * @brief        获取Enum型参数值
         * @param        strKey                IN        参数键值，具体键值名称参考HikCameraNode.xls文档
         * @param        pnValue               OUT       返回值
         * @return       成功：ture；错误：false
         ****************************************************************************/
        private bool GetEnumValue(string strKey, ref UInt32 pnValue)
        {
            MyCamera.MVCC_ENUMVALUE stParam = new MyCamera.MVCC_ENUMVALUE();
            int nRet = m_pMyCamera.MV_CC_GetEnumValue_NET(strKey, ref stParam);
            if (MyCamera.MV_OK != nRet)
            {
                return false;
            }
            pnValue = stParam.nCurValue;
            return true;
        }
        /************************************************************************
        *  @fn     IsMonoData()
        *  @brief  判断是否是彩色数据
        *  @param  enGvspPixelType         [IN]           像素格式
        *  @return 成功，返回0；错误，返回-1 
        ************************************************************************/
        private bool IsMonoData(MyCamera.MvGvspPixelType enGvspPixelType)
        {
            switch (enGvspPixelType)
            {
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10_Packed:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12:
                case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12_Packed:
                    return true;

                default:
                    return false;
            }
        }
        /****************************************************************************
         * @fn           SaveImage
         * @brief        保存图片
         * @param        pSaveParam            IN        保存图片配置参数结构体
         * @return       成功：true；错误：false
        ****************************************************************************/
        public bool ConvertPixelType(MyCamera.MV_PIXEL_CONVERT_PARAM stConverPixelParam)
        {
            int nRet;
            nRet = m_pMyCamera.MV_CC_ConvertPixelType_NET(ref stConverPixelParam);
            if (MyCamera.MV_OK != nRet)
            {
                return false;
            }
            return true;
        }
        public void TriggerBySoftware()
        {
            int nRet;

            // ch:触发命令 | en:Trigger command
            nRet = m_pMyCamera.MV_CC_SetCommandValue_NET("TriggerSoftware");
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("Trigger Fail!");
            }
        }

        /// <summary>
        /// IO输出 打开Ling1 关闭Line2
        /// </summary>
        /// <exception cref="Exception"></exception>
        public bool OpenLine1CloseLine2()
        {
            int nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("LineSelector", 1);      //IO Line2
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("IO Line2设置失败！");
            }
            nRet = m_pMyCamera.MV_CC_SetBoolValue_NET("StrobeEnable", true);      //strobe使能
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("设置strobe使能失败！");
            }

            nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("LineSelector", 2);      //IO Line2
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("IO Line2设置失败！");
            }
            nRet = m_pMyCamera.MV_CC_SetBoolValue_NET("StrobeEnable", false);      //strobe使能
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("设置strobe使能失败！");
            }

            return true;
        }

        /// <summary>
        /// IO输出 打开Ling2 关闭Line1
        /// </summary>
        /// <exception cref="Exception"></exception>
        public bool OpenLine2CloseLine1()
        {
            int nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("LineSelector", 1);      //IO Line2
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("IO Line2设置失败！");
            }
            nRet = m_pMyCamera.MV_CC_SetBoolValue_NET("StrobeEnable", false);      //strobe使能
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("设置strobe使能失败！");
            }

            nRet = m_pMyCamera.MV_CC_SetEnumValue_NET("LineSelector", 2);      //IO Line2
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("IO Line2设置失败！");
            }
            nRet = m_pMyCamera.MV_CC_SetBoolValue_NET("StrobeEnable", true);      //strobe使能
            if (MyCamera.MV_OK != nRet)
            {
                throw new Exception("设置strobe使能失败！");
            }

            return true;
        }

        public void Start()
        {
            StartCrab();
        }

        public void Stop()
        {
            StopCrab();
        }
    }
}
